

/*-------------------------------------------------------------------------
 *
 * parse_collate.c
 *		Routines for assigning collation information.
 *
 * 我们选择在表达式解析分析的输出上进行后续处理，以处理排序信息。
 * 这是因为我们需要比成型树中所需的更多状态来执行此处理。
 * 如果我们在构建树的同时进行，这些状态就必须在表达式节点树中永久保存。
 * 这样，额外的存储只是这个递归例程中的局部变量。
 *
 * 实际上保存在完成树中的信息是：
 * 1. 每个表达式节点的输出排序，或者如果返回非可排序数据类型则为 InvalidOid。
 * 如果结果类型是可排序的，但排序是不确定的，这也可以是 InvalidOid。
 * 2. 在执行每个函数时使用的排序。InvalidOid 表示没有可排序的输入，或者它们的排序是
 * 不确定的。这个值只存储在可能调用使用排序函数的节点类型中。
 *
 * 你可能会认为我们只需要每个节点存储一个排序，但这两个概念确实需要保持区分。
 * 否则，当一个函数产生可排序输出类型但没有可排序输入，或者从可排序输入产生
 * 非可排序输出时，会变得太困惑。
 *
 * 不确定排序的情况可能导致在运行时抛出错误。如果我们准确地知道哪些函数需要
 * 排序信息，我们可以在解析时抛出这些错误。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/parser/parse_collate.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "catalog/pg_aggregate.h"
#include "catalog/pg_collation.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "parser/parse_collate.h"
#include "utils/lsyscache.h"



/*
 * 排序强度（SQL标准称之为“推导”）。顺序的选择是为了
 * 使比较能够有效地工作。注意：标准似乎没有
 * 区分NONE和CONFLICT。
 */
typedef enum
{
	COLLATE_NONE,				/* 表达式的类型为非可排序数据类型 */
	COLLATE_IMPLICIT,			/* 排序是通过隐式推导得出的 */
	COLLATE_CONFLICT,			/* 我们遇到了隐式排序的冲突 */
	COLLATE_EXPLICIT			/* 排序是通过显式推导得出的 */
} CollateStrength;

typedef struct
{
	ParseState *pstate;			/* 解析状态（用于错误报告） */
	Oid			collation;		/* 当前排序规则的 OID（如果有） */
	CollateStrength strength;	/* 当前排序选择的强度 */
	int			location;		/* 设置排序规则的表达式位置 */
	/* 当 strength == COLLATE_CONFLICT 时，剩余字段仅有效 */
	Oid			collation2;		/* 有冲突的排序规则的 OID */
	int			location2;		/* 设置 collation2 的表达式位置 */
} assign_collations_context;

static bool fc_assign_query_collations_walker(Node *fc_node, ParseState *fc_pstate);
static bool fc_assign_collations_walker(Node *fc_node,
									 assign_collations_context *fc_context);
static void fc_merge_collation_state(Oid fc_collation,
								  CollateStrength fc_strength,
								  int fc_location,
								  Oid fc_collation2,
								  int fc_location2,
								  assign_collations_context *fc_context);
static void fc_assign_aggregate_collations(Aggref *fc_aggref,
										assign_collations_context *fc_loccontext);
static void fc_assign_ordered_set_collations(Aggref *fc_aggref,
										  assign_collations_context *fc_loccontext);
static void fc_assign_hypothetical_collations(Aggref *fc_aggref,
										   assign_collations_context *fc_loccontext);


/*
 * assign_query_collations()
 *		标记给定查询中所有表达式的排序信息。
 *
 * 这应该在解析分析完成后应用于每个查询
 * 以处理表达式。请注意，我们不会递归进入子查询，因为
 * 它们应该在构建时被处理。
 */
void assign_query_collations(ParseState *fc_pstate, Query *fc_query)
{
	/*
	 * 我们只使用 query_tree_walker() 来访问所有包含的表达式。
	 * 不过，我们可以跳过 rangetable 和 CTE 子查询，因为 RTE 和
	 * 子查询最好已经处理过（否则引用它们的 Vars 将无法
	 * 以正确的排序创建）。
	 */
	(void) query_tree_walker(fc_query,
							 fc_assign_query_collations_walker,
							 (void *) fc_pstate,
							 QTW_IGNORE_RANGE_TABLE |
							 QTW_IGNORE_CTE_SUBQUERIES);
}

/*
 * Walker for assign_query_collations
 *
 * query_tree_walker 找到的每个表达式都是独立处理的。
 * 请注意，query_tree_walker 可能会传给我们一个整个 List，例如
 * targetlist，在这种情况下每个子表达式必须独立处理
 * ——如果两个不同的 targetentries 具有不同的排序则我们不想重复。
 */
static bool fc_assign_query_collations_walker(Node *fc_node, ParseState *fc_pstate)
{
	/* 对于空子表达式无需进行任何操作 */
	if (fc_node == NULL)
		return false;

	/*
	 * 我们不想递归进入一个集合操作树；它已经在 transformSetOperationStmt 中
	 * 完全处理过。
	 */
	if (IsA(fc_node, SetOperationStmt))
		return false;

	if (IsA(fc_node, List))
		assign_list_collations(fc_pstate, (List *) fc_node);
	else
		assign_expr_collations(fc_pstate, fc_node);

	return false;
}

/*
 * assign_list_collations()
 *		标记表达式列表中所有节点的排序信息。
 *
 * 列表成员表达式是独立处理的；它们不必共享一个共同的排序。
 */
void assign_list_collations(ParseState *fc_pstate, List *fc_exprs)
{
	ListCell   *fc_lc;

	foreach(fc_lc, fc_exprs)
	{
		Node	   *fc_node = (Node *) lfirst(fc_lc);

		assign_expr_collations(fc_pstate, fc_node);
	}
}

/*
 * assign_expr_collations()
 *		标记给定表达式树中所有节点的排序信息。
 *
 * 这为处理表达式的各种工具命令而导出，而无需构建完整的查询。
 * 应在调用 transformExpr() 及任何表达式修改操作（如
 * coerce_to_boolean()）之后应用。
 */
void assign_expr_collations(ParseState *fc_pstate, Node *fc_expr)
{
	assign_collations_context fc_context;

	/* 初始化树遍历的上下文 */
	fc_context.pstate = fc_pstate;
	fc_context.collation = InvalidOid;
	fc_context.strength = COLLATE_NONE;
	fc_context.location = -1;

	/* 然后我们开始进行遍历 */
	(void) fc_assign_collations_walker(fc_expr, &fc_context);
}

/*
 * select_common_collation()
 *		为一组表达式识别一个共同的排序。
 *
 * 这些表达式应返回相同的数据类型，否则这将没有太大意义。
 *
 * none_ok 表示可以返回 InvalidOid，指示无法识别任何共同的排序，即使对于可排序的数据类型。
 * 否则，将因隐式排序的冲突抛出错误。
 *
 * 理论上，none_ok = true 反映 SQL 标准子句 “数据类型组合的结果” 的规则，
 * none_ok = false 反映 “排序确定” 子句的规则
 * （在某些情况下通过 “分组操作” 调用）。
 */
Oid select_common_collation(ParseState *fc_pstate, List *fc_exprs, bool fc_none_ok)
{
	assign_collations_context fc_context;

	/* 初始化树遍历的上下文 */
	fc_context.pstate = fc_pstate;
	fc_context.collation = InvalidOid;
	fc_context.strength = COLLATE_NONE;
	fc_context.location = -1;

	/* 然后我们开始进行遍历 */
	(void) fc_assign_collations_walker((Node *) fc_exprs, &fc_context);

	/* 处理排序冲突 */
	if (fc_context.strength == COLLATE_CONFLICT)
	{
		if (fc_none_ok)
			return InvalidOid;
		ereport(ERROR,
				(errcode(ERRCODE_COLLATION_MISMATCH),
				 errmsg("collation mismatch between implicit collations \"%s\" and \"%s\"",
						get_collation_name(fc_context.collation),
						get_collation_name(fc_context.collation2)),
				 errhint("You can choose the collation by applying the COLLATE clause to one or both expressions."),
				 parser_errposition(fc_context.pstate, fc_context.location2)));
	}

	/*
	 * 注意：如果 strength 仍为 COLLATE_NONE，我们将返回 InvalidOid，但
	 * 这没关系，因为这必须意味着没有任何表达式返回可排序的数据类型。
	 */
	return fc_context.collation;
}


/*
 * assign_collations_walker()
 *		排序处理的递归核心。
 *
 * 没有子节点的节点（例如，Vars、Consts、Params）在构建时必须已被标记。
 * 所有上层节点在这里被标记。
 *
 * 注意：如果直接对 List 调用此函数，它将尝试推断所有列表成员的
 * 公共排序规则。特别是，如果不同成员存在冲突的显式排序规则， 
 * 它会抛出错误。
 */
static bool fc_assign_collations_walker(Node *fc_node, assign_collations_context *fc_context)
{
	assign_collations_context fc_loccontext;
	Oid			fc_collation;
	CollateStrength fc_strength;
	int			fc_location;

	/* 对于空子表达式无需进行任何操作 */
	if (fc_node == NULL)
		return false;

	/*
	 * 为递归做好准备。对于大多数节点类型（尽管不是所有），
	 * 我们首先做的事情是递归处理此节点以下的所有节点。
	 * 树的每一层都有自己的本地上下文。
	 */
	fc_loccontext.pstate = fc_context->pstate;
	fc_loccontext.collation = InvalidOid;
	fc_loccontext.strength = COLLATE_NONE;
	fc_loccontext.location = -1;
	/* 仅设置这些字段以抑制未初始化值警告： */
	fc_loccontext.collation2 = InvalidOid;
	fc_loccontext.location2 = -1;

	/*
	 * 如果合适则递归，然后确定此节点的排序规则。
	 *
	 * 注意：一般案例在 switch 的底部，在各种特殊情况下之后。
	 */
	switch (nodeTag(fc_node))
	{
		case T_CollateExpr:
			{
				/*
				 * COLLATE 设置一个显式派生的排序规则，无论
				 * 子状态是什么。但我们必须递归以设置下面的
				 * 排序信息。
				 */
				CollateExpr *fc_expr = (CollateExpr *) fc_node;

				(void) expression_tree_walker(fc_node,
											  fc_assign_collations_walker,
											  (void *) &fc_loccontext);

				fc_collation = fc_expr->collOid;
				Assert(OidIsValid(fc_collation));
				fc_strength = COLLATE_EXPLICIT;
				fc_location = fc_expr->location;
			}
			break;
		case T_FieldSelect:
			{
				/*
				 * 对于 FieldSelect，结果具有字段声明的
				 * 排序规则，无论参数中发生了什么。
				 * （直接参数必须是复合类型，因此不
				 * 可排序。）该字段的排序规则已被查找并
				 * 保存到节点中。
				 */
				FieldSelect *fc_expr = (FieldSelect *) fc_node;

				/* ... 但首先，递归 */
				(void) expression_tree_walker(fc_node,
											  fc_assign_collations_walker,
											  (void *) &fc_loccontext);

				if (OidIsValid(fc_expr->resultcollid))
				{
					/* 节点的结果类型是可排序的。 */
					/* 传递字段的排序规则作为隐式选择。 */
					fc_collation = fc_expr->resultcollid;
					fc_strength = COLLATE_IMPLICIT;
					fc_location = exprLocation(fc_node);
				}
				else
				{
					/* 节点的结果类型不可排序。 */
					fc_collation = InvalidOid;
					fc_strength = COLLATE_NONE;
					fc_location = -1;	/* 不会被使用 */
				}
			}
			break;
		case T_RowExpr:
			{
				/*
				 * RowExpr 是一个特殊情况，因为子表达式是
				 * 独立的：如果其中一些具有
				 * 不兼容的显式排序规则，我们不想抱怨。
				 */
				RowExpr    *fc_expr = (RowExpr *) fc_node;

				assign_list_collations(fc_context->pstate, fc_expr->args);

				/*
				 * 由于结果始终是复合的，因此永远没有
				 * 排序规则，我们可以在这里停止：该节点对
				 * 其父节点的排序规则没有影响。
				 */
				return false;	/* 完成 */
			}
		case T_RowCompareExpr:
			{
				/*
				 * 对于 RowCompare，我们必须找到
				 * 每对输入列的共同排序规则并构建一个列表。 如果我们找不到
				 * 共同排序规则，我们就将 InvalidOid 放入列表中，这可能会导致运行时错误，也可能不会。
				 */
				RowCompareExpr *fc_expr = (RowCompareExpr *) fc_node;
				List	   *fc_colls = NIL;
				ListCell   *fc_l;
				ListCell   *fc_r;

				forboth(fc_l, fc_expr->largs, fc_r, fc_expr->rargs)
				{
					Node	   *fc_le = (Node *) lfirst(fc_l);
					Node	   *fc_re = (Node *) lfirst(fc_r);
					Oid			fc_coll;

					fc_coll = select_common_collation(fc_context->pstate,
												   list_make2(fc_le, fc_re),
												   true);
					fc_colls = lappend_oid(fc_colls, fc_coll);
				}
				fc_expr->inputcollids = fc_colls;

				/*
				 * 由于结果始终为布尔值，因此永远没有
				 * 排序规则，我们可以在这里停止：该节点对
				 * 其父节点的排序规则没有影响。
				 */
				return false;	/* 完成 */
			}
		case T_CoerceToDomain:
			{
				/*
				 * 如果域声明包含了非默认的 COLLATE
				 * 规范，那么将该排序用作强制转换的输出排序。
				 * 否则，允许输入排序向上传递。
				 * （输入应该是域的基本类型，因此我们不需要担心
				 * 当域存在时，它不会可排序。）
				 */
				CoerceToDomain *fc_expr = (CoerceToDomain *) fc_node;
				Oid			fc_typcollation = get_typcollation(fc_expr->resulttype);

				/* ... 但首先，递归 */
				(void) expression_tree_walker(fc_node,
											  fc_assign_collations_walker,
											  (void *) &fc_loccontext);

				if (OidIsValid(fc_typcollation))
				{
					/* 节点的结果类型是可排序的。 */
					if (fc_typcollation == DEFAULT_COLLATION_OID)
					{
						/* 排序状态从子节点向上传递。 */
						fc_collation = fc_loccontext.collation;
						fc_strength = fc_loccontext.strength;
						fc_location = fc_loccontext.location;
					}
					else
					{
						/* 将域的排序用作隐式选择。 */
						fc_collation = fc_typcollation;
						fc_strength = COLLATE_IMPLICIT;
						fc_location = exprLocation(fc_node);
					}
				}
				else
				{
					/* 节点的结果类型不可排序。 */
					fc_collation = InvalidOid;
					fc_strength = COLLATE_NONE;
					fc_location = -1;	/* 不会被使用 */
				}

				/*
				 * 将状态保存到表达式节点中。我们知道它
				 * 不关心输入排序。
				 */
				if (fc_strength == COLLATE_CONFLICT)
					exprSetCollation(fc_node, InvalidOid);
				else
					exprSetCollation(fc_node, fc_collation);
			}
			break;
		case T_TargetEntry:
			(void) expression_tree_walker(fc_node,
										  fc_assign_collations_walker,
										  (void *) &fc_loccontext);

			/*
			 * TargetEntry 只能有一个子节点，并且应该将该
			 * 状态向上传递给其父节点。我们不能使用下面的通用情况代码
			 * 因为 exprType 和相关函数在 TargetEntry 上不起作用。
			 */
			fc_collation = fc_loccontext.collation;
			fc_strength = fc_loccontext.strength;
			fc_location = fc_loccontext.location;

			/*
			 * 如果 TargetEntry 的排序是不确定的，
			 * 则抛出错误，这个 TargetEntry 是排序/分组目标。
			 * 我们更愿意现在这样做，而不是让比较函数在运行时失败，
			 * 因为我们可以给出语法错误指针以帮助定位问题。
			 * 有些情况下可能不会出现失败，例如如果规划器选择使用哈希聚合而不是
			 * 排序进行分组；但看来预测性地抛出错误似乎更好。
			 * （比较 transformSetOperationTree，它将
			 * 对于集合操作列的不确定排序抛出错误，即使
			 * 规划器能够在不排序的情况下实现集合操作。）
			 */
			if (fc_strength == COLLATE_CONFLICT &&
				((TargetEntry *) fc_node)->ressortgroupref != 0)
				ereport(ERROR,
						(errcode(ERRCODE_COLLATION_MISMATCH),
						 errmsg("collation mismatch between implicit collations \"%s\" and \"%s\"",
								get_collation_name(fc_loccontext.collation),
								get_collation_name(fc_loccontext.collation2)),
						 errhint("You can choose the collation by applying the COLLATE clause to one or both expressions."),
						 parser_errposition(fc_context->pstate,
											fc_loccontext.location2)));
			break;
		case T_InferenceElem:
		case T_RangeTblRef:
		case T_JoinExpr:
		case T_FromExpr:
		case T_OnConflictExpr:
		case T_SortGroupClause:
		case T_MergeAction:
			(void) expression_tree_walker(fc_node,
										  fc_assign_collations_walker,
										  (void *) &fc_loccontext);

			/*
			 * 当我们在查询的联接树上被调用时，我们不需要对
			 * 联接节点做任何事情，只需要递归遍历它们
			 * 以处理 WHERE/ON 表达式。因此就在这里停止。
			 * 同样，当在排序/分组列表上被调用时我们也不需要做任何事情。
			 */
			return false;
		case T_Query:
			{
				/*
				 * 当我们在属于 SubLink 的查询上被调用时，我们在这里。
				 * 假装该查询返回其第一个输出列，这确实是
				 * EXPR_SUBLINK 和 ARRAY_SUBLINK 情况下的行为。
				 * 在 SubLink 返回布尔值的情况下，此信息将被忽略。
				 * 特殊情况：在 EXISTS 中，该查询可能不返回任何列，
				 * 在这种情况下，我们需要什么都不做。
				 *
				 * 我们不需要递归，因为查询已经被处理过。
				 */
				Query	   *fc_qtree = (Query *) fc_node;
				TargetEntry *fc_tent;

				if (fc_qtree->targetList == NIL)
					return false;
				fc_tent = linitial_node(TargetEntry, fc_qtree->targetList);
				if (fc_tent->resjunk)
					return false;

				fc_collation = exprCollation((Node *) fc_tent->expr);
				/* 如果转换为数组，排序规则不会改变 */
				fc_strength = COLLATE_IMPLICIT;
				fc_location = exprLocation((Node *) fc_tent->expr);
			}
			break;
		case T_List:
			(void) expression_tree_walker(fc_node,
										  fc_assign_collations_walker,
										  (void *) &fc_loccontext);

			/*
			 * 在处理列表时，排序状态只是从
			 * 列表元素向上传递。
			 */
			fc_collation = fc_loccontext.collation;
			fc_strength = fc_loccontext.strength;
			fc_location = fc_loccontext.location;
			break;

		case T_Var:
		case T_Const:
		case T_Param:
		case T_CoerceToDomainValue:
		case T_CaseTestExpr:
		case T_SetToDefault:
		case T_CurrentOfExpr:

			/*
			 * 对于没有子节点的表达式节点的通用情况。
			 * 这些应该已经分配了排序；这个函数不负责
			 * 在目录中查找基本情况的信息。
			 */
			fc_collation = exprCollation(fc_node);

			/*
			 * 注意：在大多数情况下，只要 type_is_collatable(exprType(node))
			 * 为真，就会分配排序；但对于引用子查询输出列的 Var，
			 * 可能无法确定唯一的排序，因此会发生例外。
			 * 如果对 Var 应用依赖排序的函数，可能会导致运行时失败。
			 */

			if (OidIsValid(fc_collation))
				fc_strength = COLLATE_IMPLICIT;
			else
				fc_strength = COLLATE_NONE;
			fc_location = exprLocation(fc_node);
			break;

		default:
			{
				/*
				 * 对于大多数具有子节点的表达式节点的通用情况。
				 * 首先递归，然后确定要分配给该节点的内容。
				 */
				Oid			fc_typcollation;

				/*
				 * 对于大多数节点类型，我们希望将所有子表达式视为相同；
				 * 但有一些例外，因此有这个内部切换。
				 */
				switch (nodeTag(fc_node))
				{
					case T_Aggref:
						{
							/*
							 * Aggref 的处理比较复杂，因此我们为其提供了
							 * 自己的函数，实际上是三个。FILTER
							 * 子句独立于其余的聚合，因此可以单独处理。
							 */
							Aggref	   *fc_aggref = (Aggref *) fc_node;

							switch (fc_aggref->aggkind)
							{
								case AGGKIND_NORMAL:
									fc_assign_aggregate_collations(fc_aggref,
																&fc_loccontext);
									break;
								case AGGKIND_ORDERED_SET:
									fc_assign_ordered_set_collations(fc_aggref,
																  &fc_loccontext);
									break;
								case AGGKIND_HYPOTHETICAL:
									fc_assign_hypothetical_collations(fc_aggref,
																   &fc_loccontext);
									break;
								default:
									elog(ERROR, "unrecognized aggkind: %d",
										 (int) fc_aggref->aggkind);
							}

							assign_expr_collations(fc_context->pstate,
												   (Node *) fc_aggref->aggfilter);
						}
						break;
					case T_WindowFunc:
						{
							/*
							 * WindowFunc 只需对其 aggfilter 子句进行特殊处理，
							 * 同聚合一样。
							 */
							WindowFunc *fc_wfunc = (WindowFunc *) fc_node;

							(void) fc_assign_collations_walker((Node *) fc_wfunc->args,
															&fc_loccontext);

							assign_expr_collations(fc_context->pstate,
												   (Node *) fc_wfunc->aggfilter);
						}
						break;
					case T_CaseExpr:
						{
							/*
							 * CaseExpr 是一个特殊情况，因为我们不
							 * 希望递归进入测试表达式（如果有的话）。
							 * 它在 transformCaseExpr 期间已被标记为排序，
							 * 而且它的排序对 CASE 的结果不相关——
							 * 只有输出表达式才相关。
							 */
							CaseExpr   *fc_expr = (CaseExpr *) fc_node;
							ListCell   *fc_lc;

							foreach(fc_lc, fc_expr->args)
							{
								CaseWhen   *fc_when = lfirst_node(CaseWhen, fc_lc);

								/*
								 * 条件表达式也不能影响 CASE 的结果排序；
								 * 但由于它们已知会产生布尔值，因此
								 * 直接在它们上递归是安全的——它们
								 * 不会更改 loccontext。
								 */
								(void) fc_assign_collations_walker((Node *) fc_when->expr,
																&fc_loccontext);
								(void) fc_assign_collations_walker((Node *) fc_when->result,
																&fc_loccontext);
							}
							(void) fc_assign_collations_walker((Node *) fc_expr->defresult,
															&fc_loccontext);
						}
						break;
					case T_SubscriptingRef:
						{
							/*
							 * 下标被视为独立的
							 * 表达式，不对节点的
							 * 排序产生贡献。只有容器和源
							 * 表达式（如果有）才会贡献。（这模拟了
							 * 旧行为，在这种情况下下标可以被认为是整数
							 *，因此不产生任何贡献。）
							 */
							SubscriptingRef *fc_sbsref = (SubscriptingRef *) fc_node;

							assign_expr_collations(fc_context->pstate,
												   (Node *) fc_sbsref->refupperindexpr);
							assign_expr_collations(fc_context->pstate,
												   (Node *) fc_sbsref->reflowerindexpr);
							(void) fc_assign_collations_walker((Node *) fc_sbsref->refexpr,
															&fc_loccontext);
							(void) fc_assign_collations_walker((Node *) fc_sbsref->refassgnexpr,
															&fc_loccontext);
						}
						break;
					default:

						/*
						 * 正常情况下：所有子表达式都均等地贡献
						 * 给 loccontext。
						 */
						(void) expression_tree_walker(fc_node,
													  fc_assign_collations_walker,
													  (void *) &fc_loccontext);
						break;
				}

				
/*
				 * 现在确定要分配给此节点的排序规则。
				 */
				fc_typcollation = get_typcollation(exprType(fc_node));
				if (OidIsValid(fc_typcollation))
				{
					/* 节点的结果是可排序的；那么它的输入呢？ */
					if (fc_loccontext.strength > COLLATE_NONE)
					{
						/* 排序状态从子节点向上传递。 */
						fc_collation = fc_loccontext.collation;
						fc_strength = fc_loccontext.strength;
						fc_location = fc_loccontext.location;
					}
					else
					{
						/*
						 * 没有任何可排序输入生成可排序输出。使用类型的排序规则（通常是
						 * DEFAULT_COLLATION_OID，但对于域可能会有所不同）。
						 */
						fc_collation = fc_typcollation;
						fc_strength = COLLATE_IMPLICIT;
						fc_location = exprLocation(fc_node);
					}
				}
				else
				{
					/* 节点的结果类型不可排序。 */
					fc_collation = InvalidOid;
					fc_strength = COLLATE_NONE;
					fc_location = -1;	/* 不会被使用 */
				}

				/*
				 * 将结果排序规则保存到表达式节点中。如果状态是 COLLATE_CONFLICT，我们将
				 * 排序规则设置为 InvalidOid，这可能会导致运行时错误。
				 */
				if (fc_strength == COLLATE_CONFLICT)
					exprSetCollation(fc_node, InvalidOid);
				else
					exprSetCollation(fc_node, fc_collation);

				/*
				 * 同样保存输入排序规则，这是此节点调用的任何函数应该使用的排序规则。
				 */
				if (fc_loccontext.strength == COLLATE_CONFLICT)
					exprSetInputCollation(fc_node, InvalidOid);
				else
					exprSetInputCollation(fc_node, fc_loccontext.collation);
			}
			break;
	}

	/*
	 * 现在，将我的信息合并到父节点的状态中。
	 */
	fc_merge_collation_state(fc_collation,
						  fc_strength,
						  fc_location,
						  fc_loccontext.collation2,
						  fc_loccontext.location2,
						  fc_context);

	return false;
}

/*
 * 将子表达式的排序状态合并到其父上下文中。
 */
static void fc_merge_collation_state(Oid fc_collation,
					  CollateStrength fc_strength,
					  int fc_location,
					  Oid fc_collation2,
					  int fc_location2,
					  assign_collations_context *fc_context)
{
	/*
	 * 如果此节点的排序强度与 *context 中的排序强度不同，则该节点要么主导于
	 * 先前的兄弟节点，要么被先前的兄弟节点主导。
	 */
	if (fc_strength > fc_context->strength)
	{
		/* 重写先前的父状态 */
		fc_context->collation = fc_collation;
		fc_context->strength = fc_strength;
		fc_context->location = fc_location;
		/* 如果适用，向上冒泡错误信息 */
		if (fc_strength == COLLATE_CONFLICT)
		{
			fc_context->collation2 = fc_collation2;
			fc_context->location2 = fc_location2;
		}
	}
	else if (fc_strength == fc_context->strength)
	{
		/* 合并，或在有排序冲突时检测错误 */
		switch (fc_strength)
		{
			case COLLATE_NONE:
				/* 空 + 空 仍然是空 */
				break;
			case COLLATE_IMPLICIT:
				if (fc_collation != fc_context->collation)
				{
					/*
					 * 非默认隐式排序规则总是优于默认。
					 */
					if (fc_context->collation == DEFAULT_COLLATION_OID)
					{
						/* 重写先前的父状态 */
						fc_context->collation = fc_collation;
						fc_context->strength = fc_strength;
						fc_context->location = fc_location;
					}
					else if (fc_collation != DEFAULT_COLLATION_OID)
					{
						/*
						 * 哎呀，我们有一个冲突。我们无法在这里抛出错误，
						 * 因为冲突可能被后来的兄弟 CollateExpr 解决，
						 * 或者父级可能根本不在乎排序规则。返回足够的信息以便
						 * 在需要时稍后抛出错误。
						 */
						fc_context->strength = COLLATE_CONFLICT;
						fc_context->collation2 = fc_collation;
						fc_context->location2 = fc_location;
					}
				}
				break;
			case COLLATE_CONFLICT:
				/* 我们仍然存在冲突... */
				break;
			case COLLATE_EXPLICIT:
				if (fc_collation != fc_context->collation)
				{
					/*
					 * 哎呀，我们有一个显式 COLLATE 子句的冲突。
					 * 在这里我们选择立即抛出错误；这就是 SQL 标准所规定的，
					 * 而且没有理由不严格。
					 */
					ereport(ERROR,
							(errcode(ERRCODE_COLLATION_MISMATCH),
							 errmsg("collation mismatch between explicit collations \"%s\" and \"%s\"",
									get_collation_name(fc_context->collation),
									get_collation_name(fc_collation)),
							 parser_errposition(fc_context->pstate, fc_location)));
				}
				break;
		}
	}
}

/*
 * Aggref 是一个特殊情况，因为仅用于排序的表达式
 * 不应被视为相互冲突或与常规参数冲突，
 * 实际上不应影响聚合结果的排序规则。
 * 我们通过对它们应用 assign_expr_collations() 来处理这一点
 * 而不是传递我们的 loccontext。
 *
 * 请注意，我们递归到每个 TargetEntry，而不是直接到其包含的
 * 表达式，所以上述 T_TargetEntry 的情况会抱怨如果我们
 * 不能为 ORDER BY 项解析出排序规则（无论它是否也是
 * 一个普通的聚合参数）。
 *
 * 我们不需要递归进入 aggorder 或 aggdistinct 列表，因为这些
 * 仅包含 SortGroupClause 节点，我们无需处理这些。
 */
static void fc_assign_aggregate_collations(Aggref *fc_aggref,
							assign_collations_context *fc_loccontext)
{
	ListCell   *fc_lc;

	/* 普通聚合没有直接参数 */
	Assert(fc_aggref->aggdirectargs == NIL);

	/* 处理聚合参数，将 resjunk 参数保持在一定距离 */
	foreach(fc_lc, fc_aggref->args)
	{
		TargetEntry *fc_tle = lfirst_node(TargetEntry, fc_lc);

		if (fc_tle->resjunk)
			assign_expr_collations(fc_loccontext->pstate, (Node *) fc_tle);
		else
			(void) fc_assign_collations_walker((Node *) fc_tle, fc_loccontext);
	}
}

/*
 * 对于有序集聚合，如何最有效地进行并不十分明确。
 * 规范定义的反向分配函数只有一个排序列，
 * 并且不返回可排序的类型，但在一般情况下这显然太过限制。
 * 我们的解决方案是认为聚合的直接参数通常有助于
 * 确定聚合自身的排序规则，而聚合参数仅在聚合
 * 设计为确切有一个聚合参数时才有贡献（即，它有一个
 * 聚合参数且不是可变参数）。如果它可以有多个
 * 聚合参数，我们将聚合参数作为独立的排序列进行处理。
 * 这避免了在类似于
 *		agg(...) within group (order by x collate "foo", y collate "bar")
 * 的情况下抛出错误，同时也保证了可变聚合的行为不会
 * 因为特定调用中有多少个排序列而有所改变。
 *
 * 否则，这与普通聚合的情况非常相似。
 */
static void fc_assign_ordered_set_collations(Aggref *fc_aggref,
							  assign_collations_context *fc_loccontext)
{
	bool		fc_merge_sort_collations;
	ListCell   *fc_lc;

	/* 只有当只能有一个时，才将排序规则合并到父级 */
	fc_merge_sort_collations = (list_length(fc_aggref->args) == 1 &&
							 get_func_variadictype(fc_aggref->aggfnoid) == InvalidOid);

	/* 直接参数（如果有的话）是 Aggref 节点的正常子项 */
	(void) fc_assign_collations_walker((Node *) fc_aggref->aggdirectargs,
									fc_loccontext);

	/* 适当地处理聚合的参数 */
	foreach(fc_lc, fc_aggref->args)
	{
		TargetEntry *fc_tle = lfirst_node(TargetEntry, fc_lc);

		if (fc_merge_sort_collations)
			(void) fc_assign_collations_walker((Node *) fc_tle, fc_loccontext);
		else
			assign_expr_collations(fc_loccontext->pstate, (Node *) fc_tle);
	}
}

/*
 * 假设集聚合则更加特殊：根据规范，我们需要
 * 统一每对假设和聚合参数的排序规则。
 * 我们需要将排序规则的选择强制传递到排序列
 * 以确保排序使用所选择的排序规则。除此之外，
 * 行为类似于常规的有序集聚合。请注意，
 * 假设的直接参数仅在其配对的聚合参数存在时
 * 才会对聚合排序规则产生影响。
 */
static void fc_assign_hypothetical_collations(Aggref *fc_aggref,
							   assign_collations_context *fc_loccontext)
{
	ListCell   *fc_h_cell = list_head(fc_aggref->aggdirectargs);
	ListCell   *fc_s_cell = list_head(fc_aggref->args);
	bool		fc_merge_sort_collations;
	int			fc_extra_args;

	/* 只有当只能有一个时，才将排序规则合并到父级 */
	fc_merge_sort_collations = (list_length(fc_aggref->args) == 1 &&
							 get_func_variadictype(fc_aggref->aggfnoid) == InvalidOid);

	/* 处理任何非假设的直接参数 */
	fc_extra_args = list_length(fc_aggref->aggdirectargs) - list_length(fc_aggref->args);
	Assert(fc_extra_args >= 0);
	while (fc_extra_args-- > 0)
	{
		(void) fc_assign_collations_walker((Node *) lfirst(fc_h_cell), fc_loccontext);
		fc_h_cell = lnext(fc_aggref->aggdirectargs, fc_h_cell);
	}

	/* 并行扫描假设参数和聚合参数 */
	while (fc_h_cell && fc_s_cell)
	{
		Node	   *fc_h_arg = (Node *) lfirst(fc_h_cell);
		TargetEntry *fc_s_tle = (TargetEntry *) lfirst(fc_s_cell);
		assign_collations_context fc_paircontext;

		/*
		 * 在这对表达式内部分配排序规则，然后
		 * 为它们选择一个公共排序规则。这应该与
		 * select_common_collation() 匹配，但我们不能直接
		 * 使用该函数，因为我们需要访问整个排序规则状态，
		 * 以便可以将其向上提升到聚合函数的级别。
		 */
		fc_paircontext.pstate = fc_loccontext->pstate;
		fc_paircontext.collation = InvalidOid;
		fc_paircontext.strength = COLLATE_NONE;
		fc_paircontext.location = -1;
		/* 仅设置这些字段以抑制未初始化值警告： */
		fc_paircontext.collation2 = InvalidOid;
		fc_paircontext.location2 = -1;

		(void) fc_assign_collations_walker(fc_h_arg, &fc_paircontext);
		(void) fc_assign_collations_walker((Node *) fc_s_tle->expr, &fc_paircontext);

		/* 处理排序冲突 */
		if (fc_paircontext.strength == COLLATE_CONFLICT)
			ereport(ERROR,
					(errcode(ERRCODE_COLLATION_MISMATCH),
					 errmsg("collation mismatch between implicit collations \"%s\" and \"%s\"",
							get_collation_name(fc_paircontext.collation),
							get_collation_name(fc_paircontext.collation2)),
					 errhint("You can choose the collation by applying the COLLATE clause to one or both expressions."),
					 parser_errposition(fc_paircontext.pstate,
										fc_paircontext.location2)));

		/*
		 * 此时，如果类型不可排序，paircontext.collation 可能为 InvalidOid；
		 * 在这种情况下无需做任何事情。如果我们确实需要
		 * 更改排序列的排序规则，请通过在排序列 TLE 中插入
		 * RelabelType 节点来执行此操作。
		 *
		 * XXX 由于几个原因，这样做相当糟糕：
		 * assign_collations_walker 不应该这样改变
		 * 表达式结构，而在解析时更改
		 * 排序规则应通过 CollateExpr 而不是 RelabelType 来表示
		 * （将 RelabelType 用于排序规则标记应该只是一个
		 * 规划者/执行器的事情）。但我们没有更好的替代方案。
		 * 特别是，注入 CollateExpr 可能导致
		 * 表达式在转储/重新加载后被不同地解释，因为我们可能
		 * 有效地将隐式排序规则提升为显式排序规则。
		 * 这个变通办法依赖于 ruleutils.c 不为 RelabelType 打印
		 * COLLATE 子句，并且可能依赖于其他一些脆弱的行为。
		 */
		if (OidIsValid(fc_paircontext.collation) &&
			fc_paircontext.collation != exprCollation((Node *) fc_s_tle->expr))
		{
			fc_s_tle->expr = (Expr *)
				makeRelabelType(fc_s_tle->expr,
								exprType((Node *) fc_s_tle->expr),
								exprTypmod((Node *) fc_s_tle->expr),
								fc_paircontext.collation,
								COERCE_IMPLICIT_CAST);
		}

		/*
		 * 如果适当，将该列的排序规则状态合并到
		 * 聚合函数中。
		 */
		if (fc_merge_sort_collations)
			fc_merge_collation_state(fc_paircontext.collation,
								  fc_paircontext.strength,
								  fc_paircontext.location,
								  fc_paircontext.collation2,
								  fc_paircontext.location2,
								  fc_loccontext);

		fc_h_cell = lnext(fc_aggref->aggdirectargs, fc_h_cell);
		fc_s_cell = lnext(fc_aggref->args, fc_s_cell);
	}
	Assert(fc_h_cell == NULL && fc_s_cell == NULL);
}
